home *** CD-ROM | disk | FTP | other *** search
/ Ham Radio 2000 #2 / Ham Radio 2000 - Volume 2.iso / HAMV2 / TCP_IP / TNOS230S / WILDMAT.C < prev    next >
Encoding:
C/C++ Source or Header  |  1997-08-18  |  5.1 KB  |  203 lines

  1. /*
  2.  * @(#)wildmat.c 1.3 87/11/06   Public Domain.
  3.  *
  4. From: rs@mirror.TMC.COM (Rich Salz)
  5. Newsgroups: net.sources
  6. Subject: Small shell-style pattern matcher
  7. Message-ID: <596@mirror.TMC.COM>
  8. Date: 27 Nov 86 00:06:40 GMT
  9.  
  10. There have been several regular-expression subroutines and one or two
  11. filename-globbing routines in mod.sources.  They handle lots of
  12. complicated patterns.  This small piece of code handles the *?[]\
  13. wildcard characters the way the standard Unix(tm) shells do, with the
  14. addition that "[^.....]" is an inverse character class -- it matches
  15. any character not in the range ".....".  Read the comments for more
  16. info.
  17.  
  18. For my application, I had first ripped off a copy of the "glob" routine
  19. from within the find(1) source, but that code is bad news:  it recurses
  20. on every character in the pattern.  I'm putting this replacement in the
  21. public domain.  It's small, tight, and iterative.  Compile with -DTEST
  22. to get a test driver.  After you're convinced it works, install in
  23. whatever way is appropriate for you.
  24.  
  25. I would like to hear of bugs, but am not interested in additions; if I
  26. were, I'd use the code I mentioned above.
  27. */
  28. /*
  29. **  Do shell-style pattern matching for ?, \, [], and * characters.
  30. **  Might not be robust in face of malformed patterns; e.g., "foo[a-"
  31. **  could cause a segmentation violation.
  32. **
  33. **  Written by Rich $alz, mirror!rs, Wed Nov 26 19:03:17 EST 1986.
  34. */
  35.  
  36. /*
  37.  * Modified 6Nov87 by John Gilmore (hoptoad!gnu) to return a "match"
  38.  * if the pattern is immediately followed by a "/", as well as \0.
  39.  * This matches what "tar" does for matching whole subdirectories.
  40.  *
  41.  * The "*" code could be sped up by only recursing one level instead
  42.  * of two for each trial pattern, perhaps, and not recursing at all
  43.  * if a literal match of the next 2 chars would fail.
  44.  */
  45.  
  46. /* Modified by Anders Klemets to take an array of pointers as an optional
  47.    argument. Each part of the string that matches '*' is returned as a
  48.    null-terminated, malloced string in this array.
  49.  */
  50. #include "global.h"
  51. #include "ctype.h"
  52.  
  53.  
  54.  
  55. #if !defined(_lint)
  56. static char rcsid[] OPTIONAL = "$Id: wildmat.c,v 1.13 1997/08/19 01:19:22 root Exp root $";
  57. #endif
  58.  
  59. static int Star (const char *s, char *p, char **argv, int single);
  60. static int haveDot (register const char *c, register int len);
  61.  
  62.  
  63.  
  64. static int
  65. haveDot (register const char *s, register int len)
  66. {
  67.     if (s != NULLCHAR)    {
  68.         while (len-- > 0)
  69.             if (*s++ == '.')
  70.                 return TRUE;
  71.     }
  72.     return FALSE;
  73. }
  74.  
  75.  
  76.  
  77. static int
  78. Star (register const char *s, register char *p, register char **argv, int single)
  79. {
  80. char const *cp = s;
  81.  
  82.     if (cp == NULLCHAR || !*cp)
  83.         return -1;
  84.  
  85.     while (wildmat(cp, p, argv) == FALSE)
  86.         if(*++cp == '\0')
  87.             return -1;
  88.     if ((single == TRUE) && haveDot(s, cp - s))
  89.         return -1;
  90.     return cp - s;
  91. }
  92.  
  93.  
  94.  
  95. int
  96. wildmat (register const char *s, register char *p, register char **argv)
  97. {
  98. register int last;
  99. register int matched;
  100. register int reverse;
  101. register int cnt;
  102. int single;
  103.  
  104.     if (!s || !p)
  105.         return FALSE;
  106.  
  107.     for(; *p; s++,p++){
  108.         switch(*p){
  109.         case '\\':
  110.             /* Literal match with following character; fall through. */
  111.             p++;
  112.             /* fall through */
  113.         default:
  114.              /*   if(*s != *p)   */
  115.              if (tolower(*s) != tolower(*p))
  116.                 return FALSE;
  117.              continue;
  118.         case '?':
  119.             /* Match anything. */
  120.             if(*s == '\0')
  121.                 return FALSE;
  122.             continue;
  123.         case '*':
  124.         case '+':
  125.             single = (*p == '+');
  126.  
  127.             /* Trailing star matches everything. */
  128.             if(argv == NULLCHARP)
  129.                 return *++p ? 1 + Star(s, p, NULLCHARP, single) : TRUE;
  130.             if(*++p == '\0'){
  131.                 cnt = (int) strlen(s);
  132.                 if ((single == TRUE) && haveDot(s, cnt))
  133.                     return FALSE;
  134.             } else {
  135.                 if((cnt = Star(s, p, argv+1, single)) == -1)
  136.                     return FALSE;
  137.             }
  138. #ifdef TEST
  139.             *argv = malloc((unsigned)cnt+1);
  140. #else
  141.             *argv = mallocw((unsigned)cnt+1);
  142. #endif
  143.             strncpy(*argv,s,(size_t)cnt);
  144.             *(*argv + cnt) = '\0';
  145.             return TRUE;
  146.         case '[':
  147.             /* [^....] means inverse character class. */
  148.             reverse = (p[1] == '^' || p[1] == '!') ? TRUE : FALSE;
  149.             if(reverse)
  150.                 p++;
  151.             for(last = 0400, matched = FALSE; *++p && *p != ']'; last = *p){
  152.                 /* This next line requires a good C compiler. */
  153.                 if(*p == '-' ? *s <= *++p && *s >= last : *s == *p)
  154.                     matched = TRUE;
  155.             }
  156.             if(matched == reverse)
  157.                 return FALSE;
  158.             continue;
  159.         }
  160.     }
  161.     /* For "tar" use, matches that end at a slash also work. --hoptoad!gnu */
  162.     return *s == '\0' || *s == '/';
  163. }
  164.  
  165.  
  166.  
  167.  
  168. #ifdef  TEST
  169. #include <stdio.h>
  170.  
  171. extern char *gets();
  172.  
  173. void
  174. main (void)
  175. {
  176. char pattern[80];
  177. char text[80];
  178. char *argv[80], *cp;
  179. int cnt;
  180.     
  181.     while (TRUE){
  182.         printf("Enter pattern:  ");
  183.         if(gets(pattern) == NULL)
  184.             break;
  185.         while (TRUE){
  186.             memset(argv, 0, 80 * sizeof (char *));
  187.             printf("Enter text:  ");
  188.             if(gets(text) == NULL)
  189.                 exit(0);
  190.             if(text[0] == '\0')
  191.                 /* Blank line; go back and get a new pattern. */
  192.                 break;
  193.             printf("      %d\n", wildmat(text, pattern, argv));
  194.             for(cnt = 0; argv[cnt] != NULLCHAR; ++cnt){
  195.                 printf("String %d is: '%s'\n",cnt,argv[cnt]);
  196.                 free(argv[cnt]);
  197.             }
  198.         }
  199.     }
  200.     exit(0);
  201. }
  202. #endif  /* TEST */
  203.